<?php
/**
 * 工厂类
 *
 * @copyright Copyright (c) 2008 www.eefocus.com
 * @author kokko<kokko313@gmail.com>
 * @package com
 * @subpackage eefocus
 * @version 1.0 2007-7-30
 */
class Factory{
	/**
	 * applicateion 配置标识名
	 * 
	 * @var public
	 */
	public $_configName;

	/**
	 * 控制器
	 *
	 * @var string
	 * @access protected
	 */
	protected $_controller;
	
	/**
	 * 操作
	 *
	 * @var string
	 * @access protected
	 */
	protected $_action;

	/**
	 * 是否缓存Application
	 *
	 * @var bool
	 * @access protected
	 */
	protected $_isCache = true;
	
	protected $_cacheFile;

	/**
	 * 构造函数
	 *
	 * @param string $configName
	 * @param bool $isCache
	 */
	public function __construct( $configName = null,$isCache = true )
    {    
    	if (!defined('LIB_PATH')) die( "System error,Please defined LIB_PATH const first!" );
		$this->_configName = !empty( $configName ) ? $configName : "index";
		$this->_configName = strtolower( $this->_configName );
		$this->_isCache = $isCache;
		
		/**/
		//定义APP_PATH
		if( !defined( 'APP_PATH' ) ){ define( 'APP_PATH',dirname( $_SERVER['SCRIPT_FILENAME'] ) ); }

		//定义VIER_PATH
		if( !defined( 'VIEW_PATH' ) ){ 
			$configName = $this->_configName.'/';
			define( 'VIEW_PATH',$configName );
		}

		//定义APP_NAME
		if( !defined( 'APP_NAME' ) ){
			$name = substr( APP_PATH,strrpos( APP_PATH,'/' )+1 );
			if( empty( $name ) ){
				$doc_root = str_replace( '\\','/',DOCUROOT );
				$now_root = dirname( $_SERVER['SCRIPT_FILENAME'] );
				$name = str_replace( $doc_root,'',$now_root );
				$name = str_replace( '/','',$name );
			}
			define( 'APP_NAME',$name ); 
		}
    }

	/**
     * 运行应用实例 入口文件使用的快捷方法
	 *
     * @access public 
     * @return void
     */
	public function run() {
		$this->init();
		$this->runMVC();
		return;
	}

	/**
	 * 初始化
	 *
	 */
	public function init(){
		$cachePath = CACHE_PATH.'/'.APP_NAME;
		$this->_cacheFile = $cachePath."/~app_{$this->_configName}.config.php";
		
		//开启项目配置缓存
		if( !$this->_isCache || !file_exists( $this->_cacheFile ) || isset( $_GET['rebuild'] ) ){
			if( !is_dir( $cachePath ) ){
				Files::mkdirs( $cachePath );
			}
			if( !is_writeable( $cachePath ) ){
				$this->_isCache = false;
			}
			conf( 'TEMP_PATH',$cachePath );
			$this->build();
		}else{
			conf( array_change_key_case( include( $this->_cacheFile ) ) );
		}
		

		// 设定错误和异常处理
		$errorHandler = conf('ERROR_HANDLER');
		$exceptionHandler = conf('EXCEPTION_HANDLER');

		if( $errorHandler!==false ){
			if( !empty( $errorHandler ) ){
				set_error_handler( $errorHandler );
			}else{
				set_error_handler( array(&$this,"appError") );
			}
		}

		if( $exceptionHandler!==false ){
			if( !empty( $exceptionHandler ) ){
				set_exception_handler( $exceptionHandler );
			}else{
				set_exception_handler( array(&$this,"appException") );
			}
		}

		//默认时区
		if( function_exists( 'date_default_timezone_set' ) )
			date_default_timezone_set( conf('TIME_ZONE') );

		//启动头输出
		$this->outPutHeader();

		//过滤器
		$filters = conf( 'AUTO_PRE_FILTER' );

		//解析重写过的url path
		$urlModel = conf( 'URL_MODEL' );
		if( $urlModel ==1 ){
			$filters = 'Url,'.$filters;
		}

		// 启动过滤器
		if( !empty( $filters ) ){
			$this->loadPreFilter( $filters );
		}

		// 加载插件 必须在Session开启之后加载插件

		//解析url模型
		$this->analyze();

		 // 定义模块和操作名称
		if(!defined('MODULE_NAME'))  // Module名称
			define('MODULE_NAME',$this->_controller );      
        if(!defined('ACTION_NAME')) // Action操作
			define('ACTION_NAME', $this->_action );    
		
		//获取语言包
		$this->getLanguage();

		//权限验证
		$this->checkAuth();
	}

	/**
	 * 运行代码
	 *
	 * @static
	 */
	public function runMVC(){
		//记录动作开始时间
		$GLOBALS['_startTime'] = microtime(TRUE);
		$action = Action::loadAction( $this->_controller );
		$cuScript = $_SERVER['SCRIPT_NAME'];
		ob_start();
		ob_implicit_flush(0); 
		$action->exec( $this->_action );
		$content = ob_get_clean();
		if( conf( 'URL_MODEL' ) > 0 ){
			$content = $this->loadAfterFilter( 'Url',$content );
		}
		//显示运行信息
		if( conf( 'SHOW_RUN_TIME' ) && conf( 'OUT_PUT_FORMAT' )=='html'  ){
			$initTime = number_format( ( $GLOBALS['_startTime'] - $GLOBALS['_initTime'] ), 3 );
			$execTime = number_format( ( $GLOBALS['_execTime'] - $GLOBALS['_startTime'] ), 3);
			$templateTime = number_format( ( $GLOBALS['_endTime'] - $GLOBALS['_execTime'] ), 3);
			$totalTime = number_format( ( $GLOBALS['_endTime'] - $GLOBALS['_initTime'] ), 3);
			$str = '<div  id="debug_run_time" class="debug_run_time">Process: %ss ( Load:0.0s Init:%ss Exec:%ss Template:%ss )</div>';
			$content .= sprintf( $str,$totalTime,$initTime,$execTime,$templateTime );
		}
		echo $content;
	}

	/**
     * 自定义错误处理
	 *
     * @access public 
     * @param int $errno 错误类型
     * @param string $errstr 错误信息
     * @param string $errfile 错误文件
     * @param int $errline 错误行数
     * @return void
     */
    public function appError($errno, $errstr, $errfile, $errline)
    {
      switch ($errno) {
          case E_ERROR: 
          case E_USER_ERROR: 
              $errorStr = "Error:[$errno] $errstr ".basename($errfile)." at $errline row.\n";
			  logMessage( $errorStr,4 );
              exit( $errorStr );
              break;
          case E_STRICT:
          case E_USER_WARNING:
          case E_USER_NOTICE:
          default: 
			$errorStr = "Notice:[$errno] $errstr ".basename($errfile)." at $errline row.\n";
			logMessage( $errorStr,1 );
            break;
      }
    }

	/**
     * 自定义异常处理
	 *
     * @access public 
     * @param mixed $e 异常对象
     */
    public function appException($error)
    {
		
		$error = $error->toString();
		if(!is_array($error)) {
            $trace = debug_backtrace();
            $e['message'] = $error;
            $e['file'] = $trace[0]['file'];
            $e['class'] = $trace[0]['class'];
            $e['function'] = $trace[0]['function'];
            $e['line'] = $trace[0]['line'];
            $traceInfo='';
            $time = date("y-m-d H:i:m");
            foreach($trace as $t)
            {
                $traceInfo .= '['.$time.'] '.$t['file'].' ('.$t['line'].') ';
                $traceInfo .= $t['class'].$t['type'].$t['function'].'(';
                $traceInfo .= implode(', ', $t['args']);
                $traceInfo .=")<br/>";
            }
            $e['trace']  = $traceInfo;
        }else {
        	$e = $error;
        }
		if( !file_exists( APP_PATH.'/Config/tpl.exception.php' ) ){
			echo "<pre>";
			print_r( $e );
			echo "</pre>";
		}else{
			include APP_PATH.'/Config/tpl.exception.php';
		}
		exit();
    }
	
	/**
	 * 解析项目语言包
	 *
	 * @access protected
	 */
	protected function getLanguage(){
		$defaultLang = conf('DEFAULT_LANGUAGE');
		//语言支持
		if(conf('LANG_SWITCH_ON')) {
			$langVar = conf('LANGUAGE_VAR');
			//在url 里面设置语言
			if(isset($_GET[$langVar])) {
				$langSet = $_GET[$langVar];
				Cookie::set( $langVar,$langSet,time()+3600);
			}elseif ( Cookie::is_set( $langVar ) ) {// 获取上次用户的选择
				$langSet = Cookie::get( $langVar );
			}else{	//自动获取语言
				if( isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
					// 启用自动侦测浏览器语言
					preg_match('/^([a-z\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
					$langSet = $matches[1];
					Cookie::set( $langVar ,$langSet,time()+3600);
				}else{
					// 采用系统设置的默认语言
					$langSet = $defaultLang;
				}
			}

			// 定义当前语言
			define('LANG_SET',$langSet);

			// 加载语言包缓存文件
			$tempFile = conf( 'TEMP_PATH' ).'/lang_'.LANG_SET.'.php';
			if(file_exists( $tempFile )) {
				// 加载语言包缓存文件
				lang( include $tempFile );
			}else{
				// 加载系统语言包
				if (file_exists(LIB_PATH.'/Lang/lang_'.LANG_SET.'.php')){
					lang(include LIB_PATH.'/Lang/lang_'.LANG_SET.'.php');
				}else{
					lang(include LIB_PATH.'/Lang/lang_'.$defaultLang.'.php');
				}
				//加载项目语言包
				if (file_exists(conf('LANGUAGE_PATH').'/lang_'.LANG_SET.'php')){
					lang(include conf('LANGUAGE_PATH').'/lang_'.LANG_SET.'php' );
				}else{
					lang(include conf('LANGUAGE_PATH').'/lang_'.$defaultLang.'php' );
				}
			}
			//缓存语言包
			if( $this->_isCache ){
				$content  = "<?php\nreturn ".var_export(lang(),true).";\n?>";
				file_put_contents( $tempFile,$content );
			}
		}else{// 加载系统语言包
			lang(include LIB_PATH.'/Lang/lang_'.$defaultLang.'.php');
		}
	}

	/**
	 * 读取配置信息 编译项目
	 *
	 * @access protected
	 */
	protected function build(){
		//加载系统配置库
		$config = include( LIB_PATH."/config.php" );
		conf( array_change_key_case( $config ) );

		//加载项目配置文件
		$appConfFile = APP_PATH."/Config/config.{$this->_configName}.php";
		if( file_exists( $appConfFile ) ){
			conf( array_change_key_case( include $appConfFile ) );
		}
		
		//缓存系统配置
		if( $this->_isCache ){
			$content = 'return '.var_export( conf(),true ).";\n";
			$content  = "<?php\n".$content."\n?>";
			file_put_contents($this->_cacheFile,$content);
		}
	}

	/**
	 * 分析控制和动作
	 * 子类可覆盖
	 *
	 * @access protected
	 */
	protected function analyze(){
		$div = conf( 'ACT_MOD_DIV' );
		if( isset( $_GET['act'] ) ){
			$act = trim( $_GET['act'] );
		}else{
			$act = conf( 'DEFAULT_MODULE' ).$div.conf( 'DEFAULT_ACTION' );
		}
		$params = explode( $div,$act,2 );
		$this->_controller = !empty( $params[0] ) ? ucfirst(strtolower($params[0])) : conf( 'DEFAULT_MODULE' );
		$this->_action = !empty( $params[1] ) ? ucfirst(strtolower($params[1])) : conf( 'DEFAULT_ACTION' );
	}
	
	/**
	 * 输出头文件
	 *
	 * @access protected
	 */
	protected function outPutHeader(){
		$_outputFormat = isset( $_REQUEST['outputformat'] ) ? strtolower( $_REQUEST['outputformat'] ) : 'html';
		conf( 'OUT_PUT_FORMAT',$_outputFormat );
		switch( $_outputFormat ){
			case 'xml':
				header("Content-type: application/xml; charset=UTF-8");
				break;
			default:
				header("Content-Type:text/html; charset=UTF-8");
				break;
		}
	}
	
	/**
     * 加载预过滤器
     * 
     * @static
     * @access public 
     * @param string $filterNames  过滤器名称
     * @param mix $param  执行参数
     * @return string
     */
	protected  function loadPreFilter($filterNames,$param=null) 
    {
        if(is_string($filterNames)) 
			$filters    =   explode(',',$filterNames);
        foreach($filters as $key=>$val) {
			if( empty( $val ) )
				continue;
			$filterClass = $val.'Filter';
            import( $filterClass );
			if(class_exists($filterClass)) {
                $filter = new $filterClass();
                $filter->preExecute( $param );            	
            }
        }
        return ;
    }
    
    /**
     * 加载后置过滤器
     * 
     * @static
     * @access public 
     * @param string $filterNames  过滤器名称
     * @param mix $param  执行参数
     * @return string
     */
    protected function loadAfterFilter($filterNames,$param ){
    	if(is_string($filterNames)) 
			$filters    =   explode(',',$filterNames);
        foreach($filters as $key=>$val) {
			if( empty( $val ) )
				continue;
			$filterClass = $val.'Filter';
            import( $filterClass );
			if(class_exists($filterClass)) {
                $filter = new $filterClass();
                $param = $filter->afterExecute( $param );
            }
        }
        return $param;
    }
	
	/**
     * 检查权限
     * 
     * @static
     * @access public 
     * @param string $controller 控制器名称
     * @param mix $action 执行操作
     * @return bool
     */
	protected function checkAuth( $controller=null,$action = null ){
		$controller = empty( $controller ) ? $this->_controller : $controller;
		$action = empty( $action ) ? $this->_action : $action;
		$controller = strtolower($controller);
		$action = strtolower($action);
		//没有权限文件
		if( !file_exists( APP_PATH.'/Config/priv.'.$this->_configName.'.php' ) ){
			return true;
		}
		//权限文件路径
		$auth = include( APP_PATH.'/Config/priv.'.$this->_configName.'.php' );
		if( isset( $auth[$controller] ) ){	//当前Action需要验证
			$authMod = $auth[$controller].',';
			if( stristr( $authMod,$action.',' )!==false && empty( $_SESSION['UserID'] ) ){
				$redirect = conf('USER_AUTH_GATEWAY');
				//如果有跳转，并且输入为HTML跳转到登录页面
				if( $redirect!='' && conf( 'OUT_PUT_FORMAT' )=='html' ){
					if( !empty( $_SERVER["REQUEST_URI"] ) ){
						$scriptName = $_SERVER["REQUEST_URI"];
					}else{
						$scriptName = $_SERVER["PHP_SELF"];
						if(!empty($_SERVER["QUERY_STRING"])) $scriptName .= '?'.$_SERVER["QUERY_STRING"];
					}
					if( strstr( $redirect,'?' )!==false ){
						$redirect .= '&';
					}else{
						$redirect .= '?';
					}
					$redirect .= 'redirect='.rawurlencode( $scriptName );
					redirect($redirect);
				}else{
					die( lang( '_VALID_ACCESS_' ) );
				}
			}
		}
	}
}//end class
?>